#!/usr/bin/env node

/**
 * 前端诊断回归测试脚本
 * 自动化执行环境检查、API测试、E2E测试等诊断步骤
 * 使用方法: npm run diag
 */

import { execSync, spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// 配置
const CONFIG = {
  timeout: 300000, // 5分钟超时
  baseUrl: 'http://localhost:3000',
  apiBaseUrl: 'http://localhost:5000/api',
  artifactsDir: path.join(__dirname, '../artifacts'),
  logLevel: 'info'
};

// 日志工具
class Logger {
  static info(message) {
    console.log(`[INFO] ${new Date().toISOString()} - ${message}`);
  }
  
  static error(message) {
    console.error(`[ERROR] ${new Date().toISOString()} - ${message}`);
  }
  
  static warn(message) {
    console.warn(`[WARN] ${new Date().toISOString()} - ${message}`);
  }
  
  static success(message) {
    console.log(`[SUCCESS] ${new Date().toISOString()} - ${message}`);
  }
}

// 工具函数
class Utils {
  static ensureDir(dirPath) {
    if (!fs.existsSync(dirPath)) {
      fs.mkdirSync(dirPath, { recursive: true });
    }
  }
  
  static writeLog(filename, content) {
    const logPath = path.join(CONFIG.artifactsDir, 'logs', filename);
    Utils.ensureDir(path.dirname(logPath));
    fs.writeFileSync(logPath, content);
  }
  
  static execWithTimeout(command, options = {}) {
    try {
      const result = execSync(command, {
        timeout: CONFIG.timeout,
        encoding: 'utf8',
        ...options
      });
      return { success: true, output: result };
    } catch (error) {
      return { 
        success: false, 
        output: error.stdout || error.message,
        error: error.stderr || error.message
      };
    }
  }
  
  static async sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// 诊断任务
class DiagnosticTasks {
  constructor() {
    this.results = {
      environment: null,
      api: null,
      e2e: null,
      summary: null
    };
  }
  
  // 1. 环境体检
  async checkEnvironment() {
    Logger.info('开始环境体检...');
    const startTime = Date.now();
    
    try {
      // Node.js版本
      const nodeVersion = Utils.execWithTimeout('node --version');
      
      // npm版本
      const npmVersion = Utils.execWithTimeout('npm --version');
      
      // 项目依赖检查
      const packageCheck = Utils.execWithTimeout('npm list --depth=0', { cwd: process.cwd() });
      
      // TypeScript检查
      const tscCheck = Utils.execWithTimeout('npx tsc --noEmit', { cwd: process.cwd() });
      
      // ESLint检查
      const lintCheck = Utils.execWithTimeout('npm run lint', { cwd: process.cwd() });
      
      // 构建测试
      const buildCheck = Utils.execWithTimeout('npm run build', { cwd: process.cwd() });
      
      const envReport = {
        timestamp: new Date().toISOString(),
        duration: Date.now() - startTime,
        system: {
          platform: os.platform(),
          arch: os.arch(),
          nodeVersion: nodeVersion.success ? nodeVersion.output.trim() : 'N/A',
          npmVersion: npmVersion.success ? npmVersion.output.trim() : 'N/A'
        },
        checks: {
          dependencies: packageCheck.success,
          typescript: tscCheck.success,
          linting: lintCheck.success,
          build: buildCheck.success
        },
        details: {
          dependenciesOutput: packageCheck.output,
          typescriptOutput: tscCheck.output,
          lintingOutput: lintCheck.output,
          buildOutput: buildCheck.output
        }
      };
      
      // 保存环境检查日志
      Utils.writeLog('environment-check.log', JSON.stringify(envReport, null, 2));
      
      this.results.environment = envReport;
      Logger.success(`环境体检完成，耗时 ${envReport.duration}ms`);
      
      return envReport;
    } catch (error) {
      Logger.error(`环境体检失败: ${error.message}`);
      throw error;
    }
  }
  
  // 2. API连通性测试
  async testApiConnectivity() {
    Logger.info('开始API连通性测试...');
    const startTime = Date.now();
    
    try {
      const apiEndpoints = [
        { name: 'reports', url: '/reports' },
        { name: 'coupons', url: '/coupons' },
        { name: 'menu', url: '/menu' },
        { name: 'dashboard', url: '/dashboard/stats' },
        { name: 'users', url: '/users' }
      ];
      
      const results = [];
      
      for (const endpoint of apiEndpoints) {
        try {
          const curlCommand = `curl -s -w "\n%{http_code}\n%{time_total}" -o response.json "${CONFIG.apiBaseUrl}${endpoint.url}"`;
          const result = Utils.execWithTimeout(curlCommand);
          
          let responseData = null;
          try {
            if (fs.existsSync('response.json')) {
              responseData = JSON.parse(fs.readFileSync('response.json', 'utf8'));
              fs.unlinkSync('response.json');
            }
          } catch (e) {
            // 忽略JSON解析错误
          }
          
          const lines = result.output.split('\n').filter(line => line.trim());
          const httpCode = lines[lines.length - 2] || '000';
          const timeTotal = lines[lines.length - 1] || '0';
          
          const testResult = {
            endpoint: endpoint.name,
            url: endpoint.url,
            success: result.success && httpCode.startsWith('2'),
            httpCode: parseInt(httpCode),
            responseTime: parseFloat(timeTotal) * 1000,
            responseData,
            error: result.success ? null : result.error
          };
          
          results.push(testResult);
          
          // 保存API响应
          if (responseData) {
            const apiDir = path.join(CONFIG.artifactsDir, 'api');
            Utils.ensureDir(apiDir);
            fs.writeFileSync(
              path.join(apiDir, `${endpoint.name}-response.json`),
              JSON.stringify(responseData, null, 2)
            );
          }
          
        } catch (error) {
          results.push({
            endpoint: endpoint.name,
            url: endpoint.url,
            success: false,
            httpCode: 0,
            responseTime: 0,
            responseData: null,
            error: error.message
          });
        }
      }
      
      const apiReport = {
        timestamp: new Date().toISOString(),
        duration: Date.now() - startTime,
        totalEndpoints: apiEndpoints.length,
        successfulEndpoints: results.filter(r => r.success).length,
        results
      };
      
      // 保存API测试结果
      Utils.writeLog('api-connectivity.log', JSON.stringify(apiReport, null, 2));
      
      this.results.api = apiReport;
      Logger.success(`API连通性测试完成，成功 ${apiReport.successfulEndpoints}/${apiReport.totalEndpoints} 个接口`);
      
      return apiReport;
    } catch (error) {
      Logger.error(`API连通性测试失败: ${error.message}`);
      throw error;
    }
  }
  
  // 3. E2E测试
  async runE2ETests() {
    Logger.info('开始E2E测试...');
    const startTime = Date.now();
    
    try {
      // 确保Playwright已安装
      const playwrightCheck = Utils.execWithTimeout('npx playwright --version');
      if (!playwrightCheck.success) {
        Logger.warn('Playwright未安装，正在安装...');
        const installResult = Utils.execWithTimeout('npx playwright install chromium');
        if (!installResult.success) {
          throw new Error('Playwright安装失败');
        }
      }
      
      // 运行E2E诊断测试
      const e2eResult = Utils.execWithTimeout(
        'npx playwright test tests/e2e-diagnostic.spec.js --project=chromium --reporter=json',
        { cwd: process.cwd() }
      );
      
      let testResults = null;
      try {
        // 尝试解析Playwright JSON报告
        const reportPath = path.join(process.cwd(), 'test-results.json');
        if (fs.existsSync(reportPath)) {
          testResults = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
        }
      } catch (e) {
        Logger.warn('无法解析E2E测试报告');
      }
      
      const e2eReport = {
        timestamp: new Date().toISOString(),
        duration: Date.now() - startTime,
        success: e2eResult.success,
        output: e2eResult.output,
        error: e2eResult.error,
        testResults,
        artifactsGenerated: {
          screenshots: fs.existsSync(path.join(CONFIG.artifactsDir, 'e2e')) ? 
            fs.readdirSync(path.join(CONFIG.artifactsDir, 'e2e')).filter(f => f.endsWith('.png')).length : 0,
          logs: fs.existsSync(path.join(CONFIG.artifactsDir, 'e2e', 'console.log')),
          networkHar: fs.existsSync(path.join(CONFIG.artifactsDir, 'e2e', 'network.har'))
        }
      };
      
      // 保存E2E测试结果
      Utils.writeLog('e2e-tests.log', JSON.stringify(e2eReport, null, 2));
      
      this.results.e2e = e2eReport;
      Logger.success(`E2E测试完成，耗时 ${e2eReport.duration}ms`);
      
      return e2eReport;
    } catch (error) {
      Logger.error(`E2E测试失败: ${error.message}`);
      throw error;
    }
  }
  
  // 4. 生成汇总报告
  async generateSummary() {
    Logger.info('生成诊断汇总报告...');
    
    const summary = {
      timestamp: new Date().toISOString(),
      version: '1.0.0',
      environment: this.results.environment,
      api: this.results.api,
      e2e: this.results.e2e,
      overall: {
        success: this.results.environment?.checks && 
                this.results.api?.successfulEndpoints > 0 && 
                this.results.e2e?.success,
        issues: [],
        recommendations: []
      }
    };
    
    // 分析问题和建议
    if (!this.results.environment?.checks?.build) {
      summary.overall.issues.push('构建失败');
      summary.overall.recommendations.push('检查TypeScript类型错误和依赖问题');
    }
    
    if (this.results.api?.successfulEndpoints === 0) {
      summary.overall.issues.push('所有API接口不可用');
      summary.overall.recommendations.push('检查后端服务是否正常运行');
    }
    
    if (!this.results.e2e?.success) {
      summary.overall.issues.push('E2E测试失败');
      summary.overall.recommendations.push('检查前端页面渲染和交互功能');
    }
    
    // 保存汇总报告
    const summaryPath = path.join(CONFIG.artifactsDir, 'summary.md');
    const markdownReport = this.generateMarkdownReport(summary);
    fs.writeFileSync(summaryPath, markdownReport);
    
    Utils.writeLog('diagnostic-summary.json', JSON.stringify(summary, null, 2));
    
    this.results.summary = summary;
    Logger.success('诊断汇总报告已生成');
    
    return summary;
  }
  
  // 生成Markdown格式报告
  generateMarkdownReport(summary) {
    return `# 前端诊断报告

生成时间: ${summary.timestamp}

## 总体状态

${summary.overall.success ? '✅ 通过' : '❌ 失败'}

## 环境检查

- Node.js版本: ${summary.environment?.system?.nodeVersion || 'N/A'}
- npm版本: ${summary.environment?.system?.npmVersion || 'N/A'}
- 依赖检查: ${summary.environment?.checks?.dependencies ? '✅' : '❌'}
- TypeScript检查: ${summary.environment?.checks?.typescript ? '✅' : '❌'}
- 代码规范检查: ${summary.environment?.checks?.linting ? '✅' : '❌'}
- 构建测试: ${summary.environment?.checks?.build ? '✅' : '❌'}

## API连通性

- 测试接口数: ${summary.api?.totalEndpoints || 0}
- 成功接口数: ${summary.api?.successfulEndpoints || 0}
- 成功率: ${summary.api ? Math.round((summary.api.successfulEndpoints / summary.api.totalEndpoints) * 100) : 0}%

## E2E测试

- 测试状态: ${summary.e2e?.success ? '✅ 通过' : '❌ 失败'}
- 截图数量: ${summary.e2e?.artifactsGenerated?.screenshots || 0}
- 控制台日志: ${summary.e2e?.artifactsGenerated?.logs ? '✅' : '❌'}
- 网络HAR: ${summary.e2e?.artifactsGenerated?.networkHar ? '✅' : '❌'}

## 发现的问题

${summary.overall.issues.length > 0 ? 
  summary.overall.issues.map(issue => `- ${issue}`).join('\n') : 
  '无重大问题'}

## 修复建议

${summary.overall.recommendations.length > 0 ? 
  summary.overall.recommendations.map(rec => `- ${rec}`).join('\n') : 
  '无特殊建议'}

## 文件清单

- 环境检查日志: \`artifacts/logs/environment-check.log\`
- API测试日志: \`artifacts/logs/api-connectivity.log\`
- E2E测试日志: \`artifacts/logs/e2e-tests.log\`
- API响应数据: \`artifacts/api/\`
- E2E截图: \`artifacts/e2e/\`
- 修复补丁: \`artifacts/patches/\`
`;
  }
}

// 主函数
async function main() {
  Logger.info('开始前端诊断流程...');
  const startTime = Date.now();
  
  try {
    // 确保artifacts目录存在
    Utils.ensureDir(CONFIG.artifactsDir);
    Utils.ensureDir(path.join(CONFIG.artifactsDir, 'logs'));
    Utils.ensureDir(path.join(CONFIG.artifactsDir, 'api'));
    Utils.ensureDir(path.join(CONFIG.artifactsDir, 'e2e'));
    Utils.ensureDir(path.join(CONFIG.artifactsDir, 'patches'));
    
    const diagnostic = new DiagnosticTasks();
    
    // 执行诊断步骤
    await diagnostic.checkEnvironment();
    await diagnostic.testApiConnectivity();
    await diagnostic.runE2ETests();
    await diagnostic.generateSummary();
    
    const totalTime = Date.now() - startTime;
    Logger.success(`诊断流程完成，总耗时 ${totalTime}ms`);
    Logger.info(`报告已保存至: ${path.join(CONFIG.artifactsDir, 'summary.md')}`);
    
    // 输出简要结果
    const summary = diagnostic.results.summary;
    console.log('\n=== 诊断结果摘要 ===');
    console.log(`总体状态: ${summary.overall.success ? '通过' : '失败'}`);
    console.log(`环境检查: ${summary.environment?.checks?.build ? '通过' : '失败'}`);
    console.log(`API连通性: ${summary.api?.successfulEndpoints || 0}/${summary.api?.totalEndpoints || 0}`);
    console.log(`E2E测试: ${summary.e2e?.success ? '通过' : '失败'}`);
    
    if (summary.overall.issues.length > 0) {
      console.log('\n发现问题:');
      summary.overall.issues.forEach(issue => console.log(`  - ${issue}`));
    }
    
    process.exit(summary.overall.success ? 0 : 1);
    
  } catch (error) {
    Logger.error(`诊断流程失败: ${error.message}`);
    process.exit(1);
  }
}

// 处理未捕获的异常
process.on('uncaughtException', (error) => {
  Logger.error(`未捕获的异常: ${error.message}`);
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  Logger.error(`未处理的Promise拒绝: ${reason}`);
  process.exit(1);
});

// 处理中断信号
process.on('SIGINT', () => {
  Logger.warn('收到中断信号，正在退出...');
  process.exit(1);
});

process.on('SIGTERM', () => {
  Logger.warn('收到终止信号，正在退出...');
  process.exit(1);
});

// 如果直接运行此脚本
if (import.meta.url === `file://${process.argv[1]}`) {
  main();
}

export { DiagnosticTasks, Utils, Logger, CONFIG };